package servlet;

import config.Action;
import config.ActionMapping;
import config.Forward;
import config.Test;
import exception.ActionNotFoundException;
import exception.ConfigDigesterErrorException;
import exception.ForwardNotFoundException;
import org.apache.commons.digester.Digester;
import org.apache.commons.digester.xmlrules.DigesterLoader;
import org.xml.sax.SAXException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author:石小俊
 * @Date:2022/12/29 9:25
 * @Version:1.0-SNAPSHOT
 * @Description:
 */
@WebServlet(urlPatterns = "*.do",loadOnStartup = 1,initParams = {
        @WebInitParam(name = "configLocation",value = "actions.xml")
})
public class ActionServlet extends HttpServlet {

    private ActionMapping actionMapping;
    private Map<String,Object> actions;

    @Override
    public void init() throws ServletException {
        actionMapping = new ActionMapping();
        actions = new HashMap<>();
        Digester digester = DigesterLoader.createDigester(Test.class.getClassLoader().getResource("config/rule.xml"));
        digester.push(actionMapping);
        try {
            // 用户的配置文件actions.xml做成初始化参数
            String configLocation = getInitParameter("configLocation");
            digester.parse(Test.class.getClassLoader().getResourceAsStream(configLocation));
        } catch (IOException e) {
            e.printStackTrace();
            throw new ConfigDigesterErrorException("配置文件初始化失败");
        } catch (SAXException e) {
            e.printStackTrace();
            throw new ConfigDigesterErrorException("配置文件初始化失败");
        }
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println(actionMapping);
        // 获取当前发送的请求
        String path = request.getServletPath();
        // 由于当前访问的请求是以.do作为后缀的
        // 但是在配置文件actions.xml中,其请求没有后缀
        // 因此,想要去判断,需要将请求的后缀截取掉
        path = path.substring(0,path.lastIndexOf("."));
        System.out.println("path:"+path);
        // 此时,可以根据用户的请求去配置文件中找到对应的配置
        // 即:根据path的值找对应的Action对象
        Action action = actionMapping.findAction(path);
        System.out.println("action:"+action);
        if(null == action){
            throw new ActionNotFoundException("请求"+path+"在配置文件中没有找到");
        }
        try {
            // 根据Action对象中的数据,通过反射找到对应的类
            Class actionClass = Class.forName(action.getType());
            // 由于在配置文件中,某些配置使用的是同一个类
            // 如果每次都创建一个新的对象,则效率太低
            // 将其做成单例
            // 创建一个Map集合,集合的key即为Action的type属性,集合的值即为反射实例化的对象
            Object obj = actions.get(action.getType());
            // 如果没有取到对应的对象,说明尚未实例化过该对象
            if(null == obj){
                // 通过反射实例化该对象
                obj = actionClass.newInstance();
                // 实例化完成之后,将实例化的对象存放到Map集合中,便于下次使用
                actions.put(action.getType(),obj);
            }

            // 对象获取到之后,我们需要访问该对象中的某一个方法
            // 根据请求找到与之对应的方法
            // 在Servlet开发中,是根据请求与响应对象进行处理的
            // 因此,我们在对应的XXXAction类中的方法中必须拥有请求与响应对象
            // 此处对方法的参数做限制
            Method method = actionClass.getMethod(action.getParameter(),HttpServletRequest.class,HttpServletResponse.class);
            System.out.println("method:"+method);
            // 获取指定方法的返回值
            String result = (String) method.invoke(obj,request,response);

            // 如果方法没有返回值,则不做响应处理,一般用于ajax操作
            if(null == result){
                System.out.println("当前方法无返回值,请使用Ajax来实现");
                return;
            }

            // 如果方法存在返回值,则根据返回值找到对应的Forward对象
            Forward forward = action.findForward(result);
            // 该返回值可能在配置文件中尚未配置
            if(null == forward){
                throw new ForwardNotFoundException("返回值"+result+"在配置文件中没有找到对应的配置");
            }

            // 找到对应Forward配置之后,根据Forward对应的redirect属性判断当前使用转发还是重定向
            if(forward.isRedirect()){
                // 如果redirect属性值值为true,则使用重定向
                // 重定向到path地址
                response.sendRedirect(request.getContextPath()+forward.getPath());
                return;
            }
            // 否则转发
            request.getRequestDispatcher(forward.getPath()).forward(request,response);

        } catch (Exception e) {
            e.printStackTrace();
            throw new ServletException(e.getMessage());
        }
    }

}
